package com.sgcc.nfc.lock.core.nfc

import kotlin.random.Random
import kotlin.text.Charsets

object NfcCommandBuilder {

    private val ZERO_IV = ByteArray(16)

    private fun randomUInt(): Int = Random.nextInt()

    fun id(): List<Int> = listOf(
        0x00080001,
        randomUInt(),
        0x09080005
    )

    fun count(): List<Int> = listOf(
        0x00080001,
        randomUInt(),
        0x03010030
    )

    fun dt(key: ByteArray): List<Int> {
        val command = listOf(
            0x00108001,
            randomUInt(),
            0x88081800.toInt(),
            0x00000000,
            (System.currentTimeMillis() / 1000).toInt()
        )
        return AesUtil.encryptUInt(command, 1, 4, key, ZERO_IV)
    }

    fun usr(key: ByteArray, name: String): List<Int> {
        val nameBytes = name.toByteArray(Charsets.UTF_8)
        val padding = run {
            val remainder = (nameBytes.size + 8) % 16
            16 - remainder
        }
        val padded = ByteArray(nameBytes.size + padding)
        System.arraycopy(nameBytes, 0, padded, 0, nameBytes.size)
        val extra = padded.size - nameBytes.size
        if (extra > 0) {
            val randomBytes = ByteArray(extra)
            Random.nextBytes(randomBytes)
            System.arraycopy(randomBytes, 0, padded, nameBytes.size, randomBytes.size)
        }

        val nameInts = mutableListOf<Int>()
        var index = 0
        while (index < padded.size) {
            val value = ((padded.getOrNull(index)?.toInt() ?: 0) and 0xFF shl 24) or
                ((padded.getOrNull(index + 1)?.toInt() ?: 0) and 0xFF shl 16) or
                ((padded.getOrNull(index + 2)?.toInt() ?: 0) and 0xFF shl 8) or
                ((padded.getOrNull(index + 3)?.toInt() ?: 0) and 0xFF)
            nameInts.add(value)
            index += 4
        }

        val totalDataBytes = 4 + 4 + nameInts.size * 4
        val dataLenPrefix = (totalDataBytes shl 16) or 0x8001
        val nameLenField = (0x91 shl 24) or ((nameBytes.size and 0xFF) shl 16) or 0x1801

        val command = mutableListOf(
            dataLenPrefix,
            randomUInt(),
            nameLenField
        )
        command.addAll(nameInts)
        return AesUtil.encryptUInt(command, 1, command.lastIndex, key, ZERO_IV)
    }

    fun chargeThreshold(key: ByteArray): List<Int> {
        val command = listOf(
            0x00108001,
            randomUInt(),
            0x05020011,
            randomUInt(),
            randomUInt()
        )
        return AesUtil.encryptUInt(command, 1, 4, key, ZERO_IV)
    }

    fun chargeCurrent(key: ByteArray): List<Int> {
        val command = listOf(
            0x00108001,
            randomUInt(),
            0x05020010,
            randomUInt(),
            randomUInt()
        )
        return AesUtil.encryptUInt(command, 1, 4, key, ZERO_IV)
    }

    fun arm(key: ByteArray, ctrl: Int): List<Int> {
        val randomTail = randomUInt() and 0x00FFFFFF
        val command = listOf(
            0x00108001,
            randomUInt(),
            0x83010121.toInt(),
            ((ctrl and 0xFF) shl 24) or randomTail,
            randomUInt()
        )
        return AesUtil.encryptUInt(command, 1, 4, key, ZERO_IV)
    }

    fun ctrl(key: ByteArray, ctrl: Int): List<Int> {
        val randomTail = randomUInt() and 0x00FFFFFF
        val command = listOf(
            0x00108001,
            randomUInt(),
            0x83010120.toInt(),
            ((ctrl and 0xFF) shl 24) or randomTail,
            randomUInt()
        )
        return AesUtil.encryptUInt(command, 1, 4, key, ZERO_IV)
    }

    fun controlProgress(key: ByteArray): List<Int> {
        val command = listOf(
            0x00108001,
            randomUInt(),
            0x03010021,
            randomUInt(),
            randomUInt()
        )
        return AesUtil.encryptUInt(command, 1, 4, key, ZERO_IV)
    }
}
